import dash
import time
import pandas as pd
import numpy as np
from dash import dcc, html
from datetime import datetime
from flask_login import logout_user
import feffery_antd_components as fac
from dash.dependencies import Input, Output, State, ALL

from server import app
from model import UserAccount, DayWorth, WeekWorth
from utils import str2md5
import os
from config import PathConfig
from peewee import SQL



@app.callback(
    [Output('index-redirect-container', 'children'),
     Output('index-personal-info-modal', 'visible')],
    Input('index-header-dropdown', 'nClicks'),
    State('index-header-dropdown', 'clickedKey'),
    prevent_initial_call=True
)
def index_dropdown_click(nClicks, clickedKey):

    if clickedKey == '退出登录':
        logout_user()
        return [
            dcc.Location(
                pathname='/login',
                id='index-redirect'
            ),
            False
        ]

    elif clickedKey == '个人资料':
        return [
            None,
            True
        ]

    return dash.no_update


@app.callback(
    Output('index-user-manage-modal', 'visible'),
    Input('index-header-dropdown', 'nClicks'),
    State('index-header-dropdown', 'clickedKey'),
    prevent_initial_call=True
)
def index_dropdown_open_user_manage(nClicks, clickedKey):

    if clickedKey == '用户管理':
        return True
    return False


@app.callback(
    Output('index-user-manage-modal-children-container', 'children'),
    Input('index-user-manage-modal', 'visible'),
    prevent_initial_call=True
)
def index_user_manage_modal_update_children(visible):

    if visible:
        records = (
            pd
            .DataFrame(
                (
                    UserAccount
                    .select(
                        UserAccount.username,
                        UserAccount.gender,
                        UserAccount.role,
                        UserAccount.register_time
                    )
                    .dicts()
                )
            )
            .assign(
                register_time=lambda df: (
                    df
                    .register_time
                    .dt
                    .strftime('%Y-%m-%d %H:%M:%S')
                ),
                key=lambda df: df.username
            )
        )

        return [
            fac.AntdSpin(
                fac.AntdTable(
                    id='index-user-manage-user-list-table',
                    data=records.to_dict('records'),
                    columns=[
                        {
                            'dataIndex': 'username',
                            'title': '用户名'
                        },
                        {
                            'dataIndex': 'gender',
                            'title': '性别'
                        },
                        {
                            'dataIndex': 'role',
                            'title': '角色'
                        },
                        {
                            'dataIndex': 'register_time',
                            'title': '注册时间'
                        }
                    ],
                    rowSelectionType='checkbox',
                    rowSelectionWidth=50,
                    bordered=True,
                    filterOptions={
                        'username': {
                            'filterMode': 'keyword'
                        }
                    },
                    style={
                        'width': '100%'
                    }
                ),
                text='更新中'
            ),

            # 辅助处理多输入 -> 相同输出型回调
            html.Div(
                [
                    dcc.Store(
                        id={
                            'type': 'index-user-manage-user-list-table-update-store',
                            'index': '新增用户'
                        }
                    ),
                    dcc.Store(
                        id={
                            'type': 'index-user-manage-user-list-table-update-store',
                            'index': '删除用户'
                        }
                    )
                ]
            ),

            # 新增用户表单modal
            fac.AntdModal(
                [
                    fac.AntdForm(
                        [
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    id='index-user-manage-add-user-username'
                                ),
                                label='用户名',
                                id='index-user-manage-add-user-username-form-item'
                            ),
                            fac.AntdFormItem(
                                fac.AntdInput(
                                    id='index-user-manage-add-user-password',
                                ),
                                label='密码',
                                id='index-user-manage-add-user-password-form-item'
                            ),
                            fac.AntdFormItem(
                                fac.AntdSelect(
                                    id='index-user-manage-add-user-role',
                                    placeholder='请选择用户角色',
                                    options=[
                                        {'label': '管理员', 'value': '管理员'},
                                        {'label': '普通用户', 'value': '普通用户'},
                                    ]
                                ),
                                label='角色',
                                id='index-user-manage-add-user-role-form-item'
                            ),
                            fac.AntdFormItem(
                                fac.AntdRadioGroup(
                                    id='index-user-manage-add-user-gender',
                                    options=[
                                        {
                                            'label': '男',
                                            'value': '男'
                                        },
                                        {
                                            'label': '女',
                                            'value': '女'
                                        }
                                    ],
                                    optionType='button',
                                    defaultValue='男'
                                ),
                                label='性别'
                            ),

                            fac.AntdButton(
                                '确认添加',
                                id='index-user-manage-add-user-confirm',
                                type='primary',
                                block=True
                            )
                        ],
                        layout='vertical'
                    )
                ],
                id='index-user-manage-add-user-modal',
                title='新增用户',
                mask=False,
                centered=True
            ),

            fac.AntdSpace(
                [
                    fac.AntdButton(
                        '新增用户',
                        id='index-user-manage-add-user',
                        type='primary'
                    ),
                    fac.AntdPopconfirm(
                        fac.AntdButton(
                            '删除选中用户',
                            type='primary',
                            danger=True
                        ),
                        id='index-user-manage-delete-user',
                        placement='top',
                        title='确认删除'
                    ),
                ],
                style={
                    'paddingTop': '50px'
                }
            ),

            # 消息提示
            html.Div(id='index-user-manage-delete-user-message-container')
        ]

    return dash.no_update


@app.callback(
    Output('index-user-manage-add-user-modal', 'visible'),
    Input('index-user-manage-add-user', 'nClicks'),
    prevent_initiall_call=True
)
def index_user_manage_add_user_modal_open(nClicks):

    if nClicks:
        return True

    return False


@app.callback(
    [Output('index-user-manage-user-list-table', 'data'),
     Output('index-user-manage-user-list-table', 'selectedRowKeys')],
    Input(
        {
            'type': 'index-user-manage-user-list-table-update-store',
            'index': ALL
        },
        'data'
    ),
    prevent_initial_call=True
)
def index_user_manage_user_list_table_update(*args):

    records = (
        pd
        .DataFrame(
            (
                UserAccount
                .select()
                .dicts()
            )
        )
        .assign(
            register_time=lambda df: (
                df
                .register_time
                .dt
                .strftime('%Y-%m-%d %H:%M:%S')
            ),
            key=lambda df: df.username
        )
    )

    return [
        records.to_dict('records'),
        []
    ]


@app.callback(
    [
        Output(
            {
                'type': 'index-user-manage-user-list-table-update-store',
                'index': '新增用户'
            },
            'data'
        ),
        Output('index-user-manage-add-user-message-container', 'children')],
    Input('index-user-manage-add-user-confirm', 'nClicks'),
    [State('index-user-manage-add-user-username', 'value'),
     State('index-user-manage-add-user-password', 'value'),
     State('index-user-manage-add-user-role', 'value'),
     State('index-user-manage-add-user-gender', 'value')],
    prevent_initiall_call=True
)
def index_user_manage_add_user(nClicks, username, password, role, gender):

    if nClicks:

        if all([nClicks, username, password, role, gender]):
            # 检查用户名是否重复
            if UserAccount.check_user_exists(username).get('message') == '用户不存在':
                (
                    UserAccount
                    .create(
                        username=username,
                        gender=gender,
                        role=role,
                        password=password,
                        password_md5=str2md5(password),
                        register_time=datetime.now()
                    )
                )

                return [
                    {
                        'timestamp': time.time()
                    },
                    fac.AntdMessage(
                        type='success',
                        content='添加成功'
                    )
                ]

            return [
                dash.no_update,
                fac.AntdMessage(
                    type='error',
                    content='用户名已存在'
                )
            ]

        return [
            dash.no_update,
            fac.AntdMessage(
                type='error',
                content='请完善信息'
            )
        ]

    return dash.no_update


@app.callback(
    [
        Output(
            {
                'type': 'index-user-manage-user-list-table-update-store',
                'index': '删除用户'
            },
            'data'
        ),
        Output('index-user-manage-delete-user-message-container', 'children')
    ],
    Input('index-user-manage-delete-user', 'confirmCounts'),
    [
        State('index-user-manage-user-list-table', 'selectedRowKeys'),
        State('index-cache-login-username', 'data'),
    ]
)
def index_user_manage_delete_selected_user(confirmCounts, selectedRowKeys, login_cache):

    if confirmCounts:
        if selectedRowKeys:
    
            (
                UserAccount
                .delete()
                .where(
                    UserAccount.username.in_(selectedRowKeys), 
                    (UserAccount.username != login_cache['username'])
                )
                .execute()
            )

            return [
                {
                    'timestamp': time.time()
                },
                fac.AntdMessage(
                    type='success',
                    content='删除完成'
                )
            ]

        return [
            dash.no_update,
            fac.AntdMessage(
                type='warning',
                content='请先选择要删除的用户'
            )
        ]

    return dash.no_update


@app.callback(
    Output('index-main-content-container', 'children'),
    Input('index-side-menu', 'currentKey'),
)
def index_render_main_content(currentKey):

    return fac.AntdSpace(
        [
            fac.AntdResult(
                status='info',
                title=f'欢迎来到{currentKey}',
                style={
                    'boxShadow': '0 6px 16px rgb(107 147 224 / 14%)'
                }
            )
        ] * 20,
        direction='vertical',
        size='large',
        style={
            'width': '100%'
        }
    )


def calculate(username, type, filepath):
    """计算各个指标的值

    Args:
        username (str): 用户名
        type (str): 日净值或周净值，day->日净值，week->周净值
        filepath (str): 文件路径

    Returns:
        List[dict]: 计算结果
    """
    assert type in ['day', 'week'], 'type 暂不支持 {}'.format(type)

    if type == 'day':
        df = (
            pd.read_excel(filepath, usecols=[0, 1]).iloc[1:, :]
            .rename(columns={'日期': 'dt', '日净值': 'value'})
        )
        df['creator'] = username
        df['create_time'] = datetime.now()
        df['four_day_value'] = df['value'].rolling(5).apply(lambda series: series.iloc[4] / series.iloc[0] - 1)
        df['four_day_value_abs'] = df['four_day_value'].apply(lambda val: 1 if val > 0 else -1 if not np.isnan(val) else np.nan)
        df['four_day_value_sum'] = df['four_day_value_abs'].rolling(9).sum()
        df['nine_day_value_sum'] = df['four_day_value_sum'].apply(lambda val: 1 if val > 0 else 0 if not np.isnan(val) else np.nan)
        df['twenty_day_value_ratio'] = df['nine_day_value_sum'].rolling(20).apply(lambda series: series.sum() / 20)
        df['sixty_day_value_ratio'] = df['nine_day_value_sum'].rolling(60).apply(lambda series: series.sum() / 60)
        df['oht_day_value_ratio'] = df['nine_day_value_sum'].rolling(120).apply(lambda series: series.sum() / 120)
        df['thf_day_value_ratio'] = df['nine_day_value_sum'].rolling(240).apply(lambda series: series.sum() / 240)
        df['value_avg'] = df.apply(
            lambda row: row['twenty_day_value_ratio']*0.4 + row['sixty_day_value_ratio']*0.3 + row['oht_day_value_ratio']*0.2 + row['thf_day_value_ratio']*0.1, 
            axis=1
        )
        df['output'] = df['value_avg'].apply(lambda val: 1 if val >= 0.55 else 0 if not np.isnan(val) else np.nan)
        df['dt'] = df['dt'].dt.strftime('%Y-%m-%d')
        df['create_time'] = df['create_time'].dt.strftime('%Y-%m-%d %H:%M:%S')
        return df.replace(np.nan, 9999999999).to_dict('records')

    df = (
        pd.read_excel(filepath, usecols=[0, 1]).iloc[1:, :]
        .rename(columns={'日期': 'dt', '周净值': 'value'})
    )
    df['creator'] = username
    df['create_time'] = datetime.now()
    df['one_week_value'] = df['value'].rolling(2).apply(lambda series: series.iloc[1] - series.iloc[0])
    df['one_week_value_abs'] = df['one_week_value'].apply(lambda val: 1 if val > 0 else -1 if not np.isnan(val) else np.nan)
    df['seven_week_value_abs'] = df['one_week_value_abs'].rolling(7).sum()
    df['seven_week_value_sum'] = df['seven_week_value_abs'].apply(lambda val: 1 if val > 0 else 0 if not np.isnan(val) else np.nan)
    df['twenty_day_value_ratio'] = df['seven_week_value_sum'].rolling(4).apply(lambda series: series.sum() / 4)
    df['sixty_day_value_ratio'] = df['seven_week_value_sum'].rolling(12).apply(lambda series: series.sum() / 12)
    df['oht_day_value_ratio'] = df['seven_week_value_sum'].rolling(24).apply(lambda series: series.sum() / 24)
    df['thf_day_value_ratio'] = df['seven_week_value_sum'].rolling(48).apply(lambda series: series.sum() / 48)
    df['value_avg'] = df.apply(
        lambda row: row['twenty_day_value_ratio']*0.4 + row['sixty_day_value_ratio']*0.3 + row['oht_day_value_ratio']*0.2 + row['thf_day_value_ratio']*0.1, 
        axis=1
    )
    df['eight_week_value'] = df['value'].rolling(9).apply(lambda series: series.iloc[-1] - series.iloc[0])
    df['eight_week_value_abs'] = df['eight_week_value'].apply(lambda val: 0.15 if val >= 0 else -0.15 if not np.isnan(val) else np.nan)
    df['eight_week_value_avg'] = df.apply(lambda row: row['value_avg'] + row['eight_week_value_abs'], axis=1)
    df['output'] = df['eight_week_value_avg'].apply(lambda val: 1 if val >= 0.55 else 0 if not np.isnan(val) else np.nan)
    df['dt'] = df['dt'].dt.strftime('%Y-%m-%d')
    df['create_time'] = df['create_time'].dt.strftime('%Y-%m-%d %H:%M:%S')
    return df.replace(np.nan, 9999999999).to_dict('records')

@app.callback(
    Output('index-message-container', 'children'),
    Input('index-upload', 'lastUploadTaskRecord'),
    State('index-cache-login-username', 'data')
)
def calculate_value(last_upload_task_record, data):
    
    triggered_id = dash.callback_context.triggered[0]['prop_id']

    if triggered_id == 'index-upload.lastUploadTaskRecord':
        task_id = last_upload_task_record['taskId']
        filename = last_upload_task_record['fileName']

        if '日净值' in filename:
            results = calculate(
                username=data['username'],
                type='day', 
                filepath=os.path.join(PathConfig.ABS_ROOT_PATH, 'caches', task_id, filename)
            )
            # 保存数据
            DayWorth.insert_many(results).execute()
            return fac.AntdMessage(
                content='计算完成',
                type='success'
            )

        elif '周净值' in filename:
            results = calculate(
                username=data['username'],
                type='week', 
                filepath=os.path.join(PathConfig.ABS_ROOT_PATH, 'caches', task_id, filename)
            )
            # 保存数据
            WeekWorth.insert_many(results).execute()
            return fac.AntdMessage(
                content='计算完成',
                type='success'
            )
        else:
            return dash.no_update

    return dash.no_update


@app.callback(
    Output('index-day-worth-table', 'data'),
    Input('index-upload', 'lastUploadTaskRecord'),
    State('index-cache-login-username', 'data')
)
def render_day_table(cache_output_data, login_data):
    # 保持渲染最新数据
    results = list(
        DayWorth
        .select(
            DayWorth.id,
            DayWorth.creator,
            SQL("strftime('%Y-%m-%d %H:%M:%S', create_time)").alias('create_time'),
            DayWorth.dt,
            DayWorth.value,
            DayWorth.output
        )
        .where(DayWorth.creator == login_data['username'])
        .order_by(DayWorth.create_time.desc(), DayWorth.dt.asc())
        .limit(10000)
        .dicts()
    )
    i = 1
    for row in results:
        row['id'] = i
        i += 1
        if row['output'] == 9999999999:
            row['output'] = '-'
        else:
            row['output'] = str(row['output']*100)+'%'

    return results


@app.callback(
    Output('index-week-worth-table', 'data'),
    Input('index-upload', 'lastUploadTaskRecord'),
    State('index-cache-login-username', 'data')
)
def render_week_table(cache_output_data, login_data):
    trigger_id = dash.ctx.triggered_id
    # 保持渲染最新数据
    results = list(
        WeekWorth
        .select(
            WeekWorth.id,
            WeekWorth.creator,
            SQL("strftime('%Y-%m-%d %H:%M:%S', create_time)").alias('create_time'),
            WeekWorth.dt,
            WeekWorth.value,
            WeekWorth.output
        )
        .where(WeekWorth.creator == login_data['username'])
        .order_by(WeekWorth.create_time.desc(), WeekWorth.dt.asc())
        .limit(10000)
        .dicts()
    )
    i = 1
    for row in results:
        row['id'] = i
        i += 1
        if row['output'] == 9999999999:
            row['output'] = '-'
        else:
            row['output'] = str(row['output']*100)+'%'

    return results